package codeine;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import javax.inject.Inject;

import org.apache.log4j.Logger;

import codeine.jsons.info.CodeineRuntimeInfo;
import codeine.jsons.project_status.ProjectStatusList;
import codeine.model.Constants;
import codeine.mongo.IStatusDatabaseConnector;
import codeine.utils.InetUtils;
import codeine.utils.ThreadUtils;

public class MongoPeerStatusUpdater implements Runnable{

	private static final Logger log = Logger.getLogger(MongoPeerStatusUpdater.class);
	@Inject private ProjectStatusList projectStatusList;
	@Inject private IStatusDatabaseConnector databaseConnector;
	@Inject private CodeineRuntimeInfo codeineRuntimeInfo;
	
	private Random random = new Random();
	private AtomicLong nextUpdate = new AtomicLong(0);
	private Object waitObject = new Object();

	public MongoPeerStatusUpdater() {
		super();
	}

	public void pushUpdate() {
		nextUpdate.set(Math.min(nextUpdate.get(), System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(5)));
		log.debug("pushUpdate next update is " + nextUpdate);
		synchronized (waitObject) {
			waitObject.notify();
		}
	}

	@Override
	public void run() {
		projectStatusList.peer_key = InetUtils.getLocalHost().getHostName() + ":" + Constants.getInstallDir();
		projectStatusList.peer_host_port = InetUtils.getLocalHost().getHostName() + ":" + codeineRuntimeInfo.port();
		ThreadUtils.sleep(TimeUnit.SECONDS.toMillis(40 + random.nextInt(30)));
		log.info("start updating");
		while (true){
			try {
				pushUpdateNow();
				waitForNextUpdate();
			} catch (Exception e) {
				log.warn("got exception", e);
			}
		}
	}

	private void waitForNextUpdate() {
		nextUpdate.set(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10));
		while (System.currentTimeMillis() < nextUpdate.get()){
			long waitTime = nextUpdate.get() - System.currentTimeMillis();
			if (waitTime < 10) {
				waitTime = 10;
			}
			log.debug("waiting " + waitTime);
			ThreadUtils.wait(waitObject, waitTime);
		}
	}

	private void pushUpdateNow() {
		log.info("pushing update now");
		databaseConnector.putReplaceStatus(projectStatusList);
	}
	
}
